package com.jeesite.modules.dataScreen.service;

import cn.hutool.core.map.MapUtil;
import com.jeesite.modules.dataScreen.entity.Tianbao;
import com.jeesite.modules.dataScreen.enums.DataTypeEnum;
import com.jeesite.modules.dataScreen.web.vo.IndustryIndexVo;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * @author machenzhi
 * @date 2024/07/04 10:23
 */
@Component
@Slf4j
public class IndustryExecutor {

    @Resource
    private IndustryIndexService industryIndexService;

    @Resource
    private TianBaoService tianBaoService;


    private static final ConcurrentMap<Class<?>, Map<String, BiConsumer<?, String>>> consumerCache = new ConcurrentHashMap<>();

    private static final ConcurrentMap<Class<?>, Map<String, List<String>>> queryFiledCache = new ConcurrentHashMap<>();

    public <R> void execute(R r, String type) {
        List<String> queryList = IndustryExecutor
                .listQueryFiledByAnnotated(r, type);
        if (Objects.isNull(queryList)) {
            log.error("IndustryExecutor execute null  type{}", type);
            return;
        }
        if (Objects.equals(DataTypeEnum.REAL_TIME.getCode(), type)) {
            Map<String, IndustryIndexVo> indexVoMap = industryIndexService.getRealtime(queryList.toArray(new String[0]));
            IndustryExecutor.setValueByMap(r, indexVoMap);
        }
        if (Objects.equals(DataTypeEnum.UNREAL_TIME.getCode(), type)) {
            Map<String, IndustryIndexVo> indexVoMap = industryIndexService.getUnRealtime(queryList.toArray(new String[0]));
            IndustryExecutor.setValueByMap(r, indexVoMap);
        }
        if (Objects.equals(DataTypeEnum.TIANBAO.getCode(), type)) {
            List<Tianbao> tianbaos = tianBaoService.listByIndexNames(queryList);

            Map<String, String> collect = tianbaos.stream()
                    .filter(tianbao -> tianbao.getIndexValue() != null)
                    .sorted(Comparator.comparing(Tianbao::getTbdate).reversed())
                    .distinct()
                    .collect(Collectors.toMap(Tianbao::getIndexName, Tianbao::getIndexValue, (o1, o2) -> o1));
            IndustryExecutor.setValueByMap(r, collect);
        }
    }


    public static <T> void setSpecialConsumer(Class<T> clazz, Map<String, BiConsumer<T, String>> specialConsumers) {
        Map<String, BiConsumer<?, String>> consumerByObjMap = getConsumerByObj(clazz);
        specialConsumers.forEach((key, value) -> consumerByObjMap.merge(key, value, (existingConsumer, newConsumer) -> newConsumer));
    }


    public static <T> Map<String, BiConsumer<?, String>> getConsumerByObj(Class<T> clazz) {
        // 首先尝试从全局缓存中获取
        Map<String, BiConsumer<?, String>> biConsumerMap = consumerCache.get(clazz);
        if (biConsumerMap != null) {
            return biConsumerMap;
        }

        // 如果缓存中没有，进行初始化
        Map<String, BiConsumer<?, String>> settersMap = new HashMap<>();

        for (Field field : clazz.getDeclaredFields()) {
            ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
            if (annotation != null) {

                String annotationValue = annotation.value();
                String setterName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                Method setterMethod;
                try {
                    setterMethod = clazz.getMethod(setterName, field.getType());
                } catch (NoSuchMethodException e) {
                    continue;
                }

                Method finalSetterMethod = setterMethod;
                BiConsumer<T, String> setterConsumer = (instance, value) -> {
                    try {
                        finalSetterMethod.invoke(instance, value);
                    } catch (Exception e) {
                        throw new RuntimeException("Error invoking setter method", e);
                    }
                };
                settersMap.put(annotationValue, setterConsumer);
            }
        }
        consumerCache.putIfAbsent(clazz, settersMap);
        return settersMap;
    }


    public static <T, U> void setValueByMap(T t, Map<String, U> map) {
        for (Map.Entry<String, U> entry : map.entrySet()) {
            U value = entry.getValue();
            String key = entry.getKey();
            Map<String, BiConsumer<?, String>> biConsumerMap = getConsumerByObj(t.getClass());
            BiConsumer<T, String> biConsumer = (BiConsumer<T, String>) biConsumerMap.get(key);
            if (value instanceof Tianbao) {
                String indexValue = ((Tianbao) value).getIndexValue();
                biConsumer.accept(t, indexValue);
            }

            if (value instanceof IndustryIndexVo) {
                String indicatorValue = ((IndustryIndexVo) value).getIndicatorValue();
                biConsumer.accept(t, indicatorValue);
            }

            if (value instanceof String) {
                biConsumer.accept(t, (String) value);
            }


        }
    }


    public static <T> List<String> listQueryFiledByAnnotated(T t, String type) {

        Class<?> clazz = t.getClass();
        Map<String, List<String>> filedMap = queryFiledCache.get(clazz);
        if (MapUtil.isNotEmpty(filedMap)) {
            return filedMap.get(type);
        }

        Map<String, List<String>> typeFieldMap = new HashMap<>();
        for (Field field : clazz.getDeclaredFields()) {
            ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
            String value = annotation.value();
            String dataType = annotation.dataType();
            typeFieldMap.computeIfAbsent(dataType, k -> new ArrayList<>()).add(value);
        }

        queryFiledCache.putIfAbsent(clazz, typeFieldMap);
        return queryFiledCache.get(clazz).get(type);
    }
}
